﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Text.RegularExpressions;

namespace XCommunity.Documents.UBB
{
  public class UbbParser
  {


    private UbbProvider _provider;

    public UbbParser( UbbProvider provider )
    {
      _provider = provider;
    }


    public UbbDocument ParseDocument( string text )
    {

      ArrayList elements = ParseUbbTags( text );

      AmendCrossBlocks( elements );

      return ParseBLocks( elements );

    }

    protected UbbDocument ParseBLocks( ArrayList elements )
    {

      Stack<IUbbContainer> blockStack = new Stack<IUbbContainer>();
      UbbDocument document = new UbbDocument();
      blockStack.Push( document );


      foreach ( object e in elements )
      {

        if ( e == null )
          throw new Exception();


        string text = e as string;
        if ( text != null )
        {
          blockStack.Peek().AppendChild( text );
          continue;
        }


        UbbElement element = e as UbbElement;
        if ( element != null )
        {
          blockStack.Peek().AppendChild( element );
          continue;
        }


        UbbBeginTag beginTag = e as UbbBeginTag;
        if ( beginTag != null )
        {
          UbbBlock block = CreateBlock( beginTag );
          blockStack.Peek().AppendChild( block );
          blockStack.Push( block );
          continue;
        }


        UbbEndTag endTag = e as UbbEndTag;
        if ( endTag != null )
        {
          UbbBlock block = blockStack.Peek() as UbbBlock;

          if ( block.UbbTag.TagName != endTag.TagName )
            throw new Exception();
          else
            blockStack.Pop();

          continue;
        }

      }

      return document;
    }

    private UbbBlock CreateBlock( UbbBeginTag beginTag )
    {
      return new UbbBlock()
      {
        UbbTag = beginTag,
      };
    }

    private void AmendCrossBlocks( ArrayList elements )
    {
      Stack<UbbBeginTag> blockStack = new Stack<UbbBeginTag>();

      for ( int i = 0; i < elements.Count; i++ )
      {
        UbbBeginTag beginTag = elements[i] as UbbBeginTag;
        UbbEndTag endTag = elements[i] as UbbEndTag;

        if ( beginTag == null && endTag == null )
          continue;

        if ( beginTag != null )
        {
          blockStack.Push( beginTag );
          continue;
        }

        if ( endTag != null )//处理结束标记
        {

          //如果所有的块都已经封闭，或者找不到对应的开始标记，那么这个结束标记是无效的。
          if ( blockStack.Count == 0 || !blockStack.Select( tag => tag.TagName ).Contains( endTag.TagName ) )
          {
            elements.Remove( endTag );
            continue;
          }


          //找出所有存在的穿越块
          List<UbbBeginTag> _beginTagList = new List<UbbBeginTag>();
          {
            UbbBeginTag _beginTag;
            while ( ( _beginTag = blockStack.Pop() ).TagName != endTag.TagName )
            {
              _beginTagList.Add( _beginTag );
            }
          }

          //对穿越的块进行封闭  -----并尝试重新接续。
          foreach ( UbbBeginTag _beginTag in _beginTagList )
          {
            elements.Insert( elements.IndexOf( endTag ), _beginTag.CreateEndTag() );
            i++;
            //signList.Insert( signList.FindIndex( sign => object.ReferenceEquals( sign, blockSign ) ) + 1, _blockSign );
          }

        }

      }

      //在文档的最后封闭所有的结束标记
      while ( blockStack.Count > 0 )
      {
        elements.Add( blockStack.Pop().CreateEndTag() );
      }

    }



    public ArrayList ParseUbbTags( string text )
    {

      text = text.Replace( "\r\n", "\n" );
      text = text.Replace( "\r", "\n" );


      //上一个UBB标签结束位置，即"]"
      int lastEndIndex = 0;

      //UBB标签开始位置，即"["
      int beginIndex = -1;

      ArrayList nodeList = new ArrayList();

      UbbBeginTag ElementBeginTag = null;

      for ( int i = 0; i < text.Length; i++ )
      {
        char ch = text[i];

        if ( ch == '[' )
          beginIndex = i;

        else if ( ch == '\n' )
        {

          //如果单行元素意外遇到换行符
          if ( ElementBeginTag != null && _provider.GetKind( ElementBeginTag.TagName ).ChildrenParseMode == UbbTagChildrenParseMode.String )
          {
            string content = text.Substring( lastEndIndex, i - lastEndIndex );

            nodeList.Add( CreateElement( ElementBeginTag, content ) );

            ElementBeginTag = null;

            lastEndIndex = i++;
          }

          beginIndex = -1;
        }

        else if ( ch == ']' && beginIndex != -1 )
        {
          //找到一个UBB标志
          string tagText = text.Substring( beginIndex + 1, i - beginIndex - 1 );

          object node = ParseUbbTag( tagText );

          if ( ElementBeginTag != null )
          {
            UbbEndTag endTag = node as UbbEndTag;

            //如果是元素的结束标志
            if ( endTag != null && endTag.TagName == ElementBeginTag.TagName )
            {
              string content = text.Substring( lastEndIndex, beginIndex - lastEndIndex );

              nodeList.Add( CreateElement( ElementBeginTag, content ) );

              ElementBeginTag = null;

              lastEndIndex = i + 1;

            }

            continue;
          }



          //如果标志前有任何文本，添加文本节点。
          if ( beginIndex > lastEndIndex )
            nodeList.Add( text.Substring( lastEndIndex, beginIndex - lastEndIndex ) );

          UbbBeginTag blockBeginTag = node as UbbBeginTag;


          if ( blockBeginTag != null )
          {
            UbbTagKind kind = _provider.GetKind( blockBeginTag.TagName );


            if ( kind.ChildrenParseMode == UbbTagChildrenParseMode.String || kind.ChildrenParseMode == UbbTagChildrenParseMode.MultilineText )
            {
              ElementBeginTag = blockBeginTag;
              node = null;
            }
          }

          beginIndex = -1;
          lastEndIndex = i + 1;

          if ( node != null )
            nodeList.Add( node );
        }
      }

      nodeList.Add( text.Substring( lastEndIndex, text.Length - lastEndIndex ) );

      return nodeList;

    }

    private UbbElement CreateElement( UbbBeginTag beginTag, string content )
    {
      return new UbbElement()
       {
         UbbTag = beginTag,
         InnerText = content
       };
    }


    Regex ubbBeginTagRegex = new Regex( "^(?<tagName>[a-zA-Z]+)(=(?<argument>.*))?$", RegexOptions.Compiled );
    Regex ubbEndTagRegex = new Regex( "^/(?<tagName>[a-zA-Z]+)$", RegexOptions.Compiled );

    private object ParseUbbTag( string signText )
    {

      if ( signText.StartsWith( "*" ) )
        return string.Format( "[{0}]", signText.Substring( 1 ) );


      Match match = ubbBeginTagRegex.Match( signText );
      if ( match.Success )
      {

        string tagName = match.Groups["tagName"].Value;
        string argument = null;
        if ( match.Groups["argument"].Success )
          argument = match.Groups["argument"].Value;

        if ( _provider.GetKind( tagName ) == null )
          return new UbbUnknowTag( signText );

        return new UbbBeginTag() { TagName = tagName, Parameter = argument };
      }


      match = ubbEndTagRegex.Match( signText );
      if ( match.Success )
      {


        string tagName = match.Groups["tagName"].Value;


        if ( _provider.GetKind( tagName ) == null )
          return new UbbUnknowTag( signText );

        return new UbbEndTag() { TagName = tagName };
      }

      return new UbbUnknowTag( signText );

    }
  }
}
